%{
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "y.tab.h"
#include "arbre.h"
#include "pile.h"
#include "parcours.h"
  struct noeud * root;

  struct pile_var * PileVar;  
  struct pile_fonc * PileFonc;  
%}

%union{
  int i;
  char* s;
  float f;
  struct noeud *n;
}

%token AND OR CLASS IF THEN ELSE END WHILE DO DEF LEQ GEQ 
%token FOR TO RETURN IN NEQ
%token <i> INT
%token <s> STRING ID 
%token <f> FLOAT
%type <n> primary multiplicative_expr additive_expr comp_expr expr exprs
%type <n> stmt lhs stmts topstmts topstmt opt_params params
%left '*' 
%left '/'
%left '+' '-'
%left '<' '>' LEQ GEQ EQ
%left AND OR
%%
program		:  topstmts opt_terms {root = $1;}
;
topstmts        : {$$ = NULL;}     
| topstmt {$$ = $1;}
| topstmts terms topstmt {$$ = creerNoeud(TOPSTMTS,$1,$3,NULL,NULL);}
;
topstmt	        : CLASS ID term stmts END {$$ = creerNoeud(CLASS,$4,NULL,NULL,NULL);setID($$,$2);}
| CLASS ID '<' ID term stmts END {$$ = creerNoeud(HERIT,$6,NULL,NULL,NULL);setID($$,$2);setHerite($$,$4);}
| stmt {$$ = $1;}
;

stmts	        : /* none */ {$$ = NULL;}
| stmt {$$ = $1;}
                | stmts terms stmt {$$ = creerNoeud(STMTS,$1,$3,NULL,NULL);} 
                ;

stmt		: IF expr THEN stmts terms END {$$ = creerNoeud(IF,$2,$4,NULL,NULL);}
                | IF expr THEN stmts terms ELSE stmts terms END {$$ = creerNoeud(IFELSE,$2,$4,$7,NULL);}
| FOR ID IN expr TO expr DO term stmts terms END 
{
  struct noeud * nid1 = creerNoeud(ID,NULL,NULL,NULL,NULL);setID(nid1,$2);
  struct noeud * nid2 = creerNoeud(ID,NULL,NULL,NULL,NULL);setID(nid2,$2);
  struct noeud * nid3 = creerNoeud(ID,NULL,NULL,NULL,NULL);setID(nid3,$2);
  struct noeud * nid4 = creerNoeud(ID,NULL,NULL,NULL,NULL);setID(nid4,$2);
  struct noeud* n1 = creerNoeud(AFFECT,nid1,$4,NULL,NULL);
  struct noeud* n2 = creerNoeud(INT,NULL,NULL,NULL,NULL);setInt(n2,1);
  struct noeud* n3 = creerNoeud(ADD,creerNoeud(CHARGER,nid2,NULL,NULL,NULL),n2,NULL,NULL);
  struct noeud* n4 = creerNoeud(AFFECT,nid3,n3,NULL,NULL);
  struct noeud* n5 = creerNoeud(STMTS,$9,n4,NULL,NULL);
  struct noeud* n6 = creerNoeud(LEQ,creerNoeud(CHARGER,nid4,NULL,NULL,NULL),$6,NULL,NULL);
  struct noeud* n7 = creerNoeud(WHILE,n6,n5,NULL,NULL);
  $$ = creerNoeud(STMTS,n1,n7,NULL,NULL);
}

/*{struct noeud* n = creerNoeud(STRING,NULL,NULL,NULL,NULL); setID(n,$2);$$ = creerNoeud(FOR,n,$4,$6,$8);}*/
| WHILE expr DO term stmts terms END {$$ = creerNoeud(WHILE,$2,$5,NULL,NULL);}
                | lhs '=' expr {$$ = creerNoeud(AFFECT,$1,$3,NULL,NULL);}
                | RETURN expr {$$ = creerNoeud(RET,$2,NULL,NULL,NULL);}
| DEF ID opt_params term stmts terms END {$$ = creerNoeud(DEF,$3,$5,NULL,NULL);setID($$,$2);}
| lhs {$$ = $1;}; 

opt_params      : /* none */ {$$ = NULL;}
| '(' ')' {$$ = NULL;}
                | '(' params ')' {$$=$2;}
;
params          : ID ',' params {$$ = creerNoeud(PARAM,$3,NULL,NULL,NULL); setID($$,$1);}
| ID {$$ = creerNoeud(PARAM,NULL,NULL,NULL,NULL);setID($$,$1);}
; 
lhs             : ID {$$ = creerNoeud(ID,NULL,NULL,NULL,NULL);setID($$,$1);}
| ID '.' primary {$$ = creerNoeud(METH,$3,NULL,NULL,NULL);setID($$,$1);}
| ID '(' exprs ')'{$$ = creerNoeud(APPEL,$3,NULL,NULL,NULL);setID($$,$1);}
;
exprs           : expr ',' exprs {$$ = creerNoeud(EXPR,$1,$3,NULL,NULL);}
| expr {$$ = $1;}
;
primary         : lhs {$$ = creerNoeud(CHARGER,$1,NULL,NULL,NULL);}
| STRING {$$ = creerNoeud(STRING,NULL,NULL,NULL,NULL); setString($$,$1);}
| FLOAT {$$ = creerNoeud(FLOAT,NULL,NULL,NULL,NULL); setFloat($$,$1);}
| INT {$$ = creerNoeud(INT,NULL,NULL,NULL,NULL); setInt($$,$1);}
| '(' expr ')' {$$=$2;}
;
expr            : expr AND comp_expr {$$ = creerNoeud(AND,$1,$3,NULL,NULL);}
| expr OR comp_expr {$$ = creerNoeud(OR,$1,$3,NULL,NULL);}
| comp_expr {$$ = $1;}
;
comp_expr       : additive_expr '<' additive_expr {$$ = creerNoeud(SI,$1,$3,NULL,NULL);}
                | additive_expr '>' additive_expr {$$ = creerNoeud(SS,$1,$3,NULL,NULL);}
                | additive_expr LEQ additive_expr {$$ = creerNoeud(LEQ,$1,$3,NULL,NULL);}
                | additive_expr GEQ additive_expr {$$ = creerNoeud(GEQ,$1,$3,NULL,NULL);}
                | additive_expr EQ additive_expr {$$ = creerNoeud(EQ,$1,$3,NULL,NULL);}
| additive_expr NEQ additive_expr {$$ = creerNoeud(NEQ,$1,$3,NULL,NULL);}
| additive_expr{$$=$1;}
;
additive_expr   : multiplicative_expr {$$=$1;}
                | additive_expr '+' multiplicative_expr {$$ = creerNoeud(ADD,$1,$3,NULL,NULL);}
| additive_expr '-' multiplicative_expr {$$ = creerNoeud(SUB,$1,$3,NULL,NULL);}
;
multiplicative_expr : multiplicative_expr '*' primary {$$ = creerNoeud(MUL,$1,$3,NULL,NULL);}
| multiplicative_expr '/' primary {$$ = creerNoeud(DIV,$1,$3,NULL,NULL);}
| primary {$$ = $1;}
;
opt_terms	: /* none */
		| terms
		;

terms		: terms ';'
                | terms '\n'
		| ';'
                | '\n'
		;
term            : ';'
                | '\n'
;
%%
int main(int argc, char * argv[]) {
  yyparse(); 
  PileVar=initPileVar();
  PileFonc=initPileFonc();
  //Ajout des prototypes pour les fonctions print
  ajouterFonc(PileFonc,"print_i32","void (i32)*",VOID);
  ajouterFonc(PileFonc,"print_double","void (double)*",VOID);
  //

  if(parcoursType(root,PileVar,PileFonc))
    {
      parcoursCode(root);
      if(argc > 1)
	{
	  FILE* f = fopen(argv[1],"w+");
	  fputs(creerMain(root->code),f);
	}
      else
	{
	  printf("%s\n",creerMain(root->code));
	}
      return 0;
    }
  else
    {
      AfficherErreur();
      return 1;
    }
}
